﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Navigant.Common;
using Navigant.Infrastructure.Cases;
using Navigant.Infrastructure.DomainBase;
using Navigant.Model.Cases;
using Navigant.Model.PreservationNotices;

namespace Navigant.Model.Custodians
{
    public class Custodian : CaseChildItem, IAggregateRoot
    {
        #region Private Fields

        private CustodianName name;
        private string email;
        private PreservationNoticeStatus? status;
        private bool isInternal;
        private bool isPerson;
        private List<PreservationNotice> history;
        private CustodianBrokenRuleMessages brokenRuleMessages;

        #endregion

        #region Constructors

        public Custodian(ICase parentCase, CustodianName name, string email) 
            : this(parentCase, name, email, 0, true, true, null)
        {
        }

        public Custodian(ICase parentCase, CustodianName name, string email, int id)
            : this(parentCase, name, email, id, true, true, null)
        {
        }

        public Custodian(ICase parentCase, CustodianName name, string email, 
            int id, bool isInternal, bool isPerson, PreservationNoticeStatus? status) 
            : base(id, parentCase)
        {
            this.name = name;
            this.email = email;
            this.isInternal = isInternal;
            this.isPerson = isPerson;
            this.status = status;
            this.brokenRuleMessages = new CustodianBrokenRuleMessages();
        }

        #endregion

        #region Properties

        public CustodianName Name
        {
            get { return this.name; }
        }

        public string Email
        {
            get { return this.email; }
        }

        public PreservationNoticeStatus Status
        {
            get
			{
				if (this.status == null)
				{	
					if (this.email == string.Empty)
					{
                        this.status = PreservationNoticeStatus.NoEmail;
					}
					else
					{
                        this.status = PreservationNoticeStatus.Unsent;

                        foreach (PreservationNotice notice in this.History)
                        {
                            if (notice.Status == PreservationNoticeStatus.Reminded ||
                                notice.Status == PreservationNoticeStatus.ReminderQueued)
                            {
                                continue;
                            }
                            else
                            {
                                this.status = notice.Status;
                            }
                        }
					}
				}
                return this.status.Value; 
            }
        }

        public bool IsInternal
        {
            get { return this.isInternal; }
            set { this.isInternal = value; }
        }

        public bool IsPerson
        {
            get { return this.isPerson; }
            set { this.isPerson = value; }
        }

        public ReadOnlyCollection<PreservationNotice> History
        {
            get 
            {
                // Lazy load implementation
                if (this.history == null)
                {
                    PreservationNoticeService service = new PreservationNoticeService(this.ParentCase);
                    this.history = new List<PreservationNotice>(service.FindBySiteAnd(this.Id));
                }
                return this.history.AsReadOnly(); 
            }
        }

        #endregion

        #region Protected Methods

        protected override void Validate()
        {
            EmailValidationResults results = EmailValidator.ValidateEmailAddress(email);
            if (results.Result != EmailValidationResult.Valid)
            {
                this.AddBrokenRule(CustodianBrokenRuleMessages.Constants.InvalidEmailAddressKey);
            }
        }

        protected override BrokenRuleMessages GetBrokenRuleMessages()
        {
            return this.brokenRuleMessages;
        }

        protected override void LoadExtendedProperties()
        {
            CustodianService service = new CustodianService(this.ParentCase);
            service.LoadExtendedProperties(this);
        }

        #endregion

        #region Public Methods

        public override string ToString()
        {
            string value = string.Empty;
            if (this.name != null)
            {
                value = this.name.ToString();
            }
            return value;
        }

        public override int GetHashCode()
        {
            return this.Id.GetHashCode();
        }

        public void NoticeSent(bool reminder)
        {
            if (!reminder)
            {
                this.SetExtendedProperty("PN-Status", PreservationNoticeStatus.NoticeQueued.ToString());
                this.SetExtendedProperty("PN-Acknowledged", null);
                this.SetExtendedProperty("PN-Sent", null);
            }
        }

        public void NoticeVerifiedSent(DateTime timestamp, bool reminder)
        {
            if (!reminder)
            {
                this.SetExtendedProperty("PN-Sent", timestamp.ToString());
                if (this.Status != PreservationNoticeStatus.Acknowledged)
                {
                    this.SetExtendedProperty("PN-Status", PreservationNoticeStatus.Sent.ToString());
                }
            }
        }

        public void NoticeAcknowledged(DateTime timestamp)
        {
            this.status = PreservationNoticeStatus.Acknowledged;
            this.SetExtendedProperty("PN-Status", PreservationNoticeStatus.Acknowledged.ToString());
            this.SetExtendedProperty("PN-Acknowledged", timestamp.ToString());
        }

        public bool CanReceiveMessages()
        {
            bool canReceiveMessages = true;

            // See if this portal has been configured to 
            // disallow messages to terminated Custodians
            if (this.ParentCase.ClientPortal.DoNotSendToTerminatedCustodians)
            {
                // Get the name of the extended property to check
                // if a Custodian has been terminated or not
                string terminatedFieldName = this.ParentCase.ClientPortal.CustodianTerminatedFieldName;

                // Make sure there was a field name defined for the termination date
                if (!string.IsNullOrEmpty(terminatedFieldName))
                {
                    // See if there is actually a termination date value populated
                    DateTime terminationDate;
                    if (this.ExtendedProperties.ContainsKey(terminatedFieldName) &&
                        DateTime.TryParse(this.ExtendedProperties[terminatedFieldName], 
                        out terminationDate))
                    {
                        // There is a termination date value, so the Custodian cannot receive messages
                        canReceiveMessages = false;
                    }
                }
            }
            return canReceiveMessages;
        }

        #region ChangeName

        public void ChangeName(CustodianName name)
        {
            if (name != null && !this.name.Equals(name))
            {
                this.name = name;
            }
        }

        #endregion

        #region ChangeEmail

        public void ChangeEmail(string emailAddress)
        {
            EmailValidationResults results = EmailValidator.ValidateEmailAddress(emailAddress);
            if (results.Result == EmailValidationResult.Valid)
            {
                this.email = emailAddress;
            }
        }

        #endregion

        #region Equality Tests

        public override bool Equals(object custodian)
        {
            return custodian != null
                && custodian is Custodian
                && this == (Custodian)custodian;
        }

        public static bool operator ==(Custodian custodian1,
            Custodian custodian2)
        {
            // check for both null (cast to object or recursive loop)
            if ((object)custodian1 == null && (object)custodian2 == null)
            {
                return true;
            }

            // check for either of them == to null
            if ((object)custodian1 == null || (object)custodian2 == null)
            {
                return false;
            }

            if (custodian1.Id != custodian2.Id)
            {
                return false;
            }

            return true;
        }

        public static bool operator !=(Custodian custodian1,
            Custodian custodian2)
        {
            return (!(custodian1 == custodian2));
        }

        #endregion

        #endregion
    }
}
